/*
 * Copyright (c) 2009, Jan Pastor. All Rights Reserved.
 */

package sk.jp.commons.exception;

import java.util.LinkedHashMap;

import sk.jp.commons.utils.CollectionUtils;
import sk.jp.commons.utils.StringUtils;

/**
 * Base exception for common application exceptions, throwed from application
 * business logic.
 * <p>
 * Use this exception (or any subclass) if client (caller component) may not
 * performs any logic if throwed, and only top layer should (frontend) shows it.
 * <p>
 * Adds i18n support.
 * 
 * @author Jan Pastor
 */
public class ApplicationException extends BaseRuntimeException implements I18nSupportable {

    private final String code;
    private final LinkedHashMap<String, Object> params;

    // ----------------------------------------------------------------------

    /**
     * Initialize exception instance.
     * @see BaseRuntimeException#BaseRuntimeException(String)
     * @param message
     */
    public ApplicationException(String message) {
        super(message);
        code = null;
        params = new LinkedHashMap<String, Object>(0);
    }

    /**
     * Initialize exception instance.
     * @see BaseRuntimeException#BaseRuntimeException(String, Throwable)
     * @param message
     * @param cause
     */
    public ApplicationException(String message, Throwable cause) {
        super(message, cause);
        code = null;
        params = new LinkedHashMap<String, Object>(0);
    }

    /**
     * Initialize exception instance.
     * @see BaseRuntimeException#BaseRuntimeException(Throwable)
     * @param cause
     */
    public ApplicationException(Throwable cause) {
        super(cause);
        code = null;
        params = new LinkedHashMap<String, Object>(0);
    }

    /**
     * Initialize exception instance.
     * @param message exception message.
     * @param code i18n message code.
     * @param params i18n message parameters. Use null if no params are used.
     * See description of {@link I18nSupportable#getParams()}.
     * @param cause exception cause.
     */
    public ApplicationException(String message, String code, LinkedHashMap<String, Object> params,
            Throwable cause) {
        super(message, cause);
        this.code = code;
        this.params = params != null ? params : new LinkedHashMap<String, Object>(0);
    }

    /**
     * Initialize exception instance without cause.
     * <p>
     * For compatibility with <i>other Ana framework</i>. Exception parameter
     * names will be 'param0', 'param1', ...
     * @param code i18n message code.
     * @param cause exception cause.
     * @param params message parameters (only values).
     */
    public ApplicationException(String code, Throwable cause, Object... params) {
        super(null, cause);
        this.code = code;
        this.params = paramsToMap(params);
    }

    /**
     * Initialize exception instance without cause.
     * @param message exception message.
     * @param code i18n message code.
     * @param params i18n message parameters. Use null if no params are used. See
     * description of {@link I18nSupportable#getParams()}.
     */
    public ApplicationException(String message, String code, LinkedHashMap<String, Object> params) {
        super(message);
        this.code = code;
        this.params = params != null ? params : new LinkedHashMap<String, Object>(0);
    }

    /**
     * Initialize exception instance.
     * <p>
     * For compatibility with <i>other Ana framework</i>. Exception parameter
     * names will be 'param0', 'param1', ...
     * <p>
     * Exception message will be set to null.
     * @param code i18n message code.
     * @param params message parameters (only values).
     */
    public ApplicationException(String code, Object... params) {
        super((String) null);
        this.code = code;
        this.params = paramsToMap(params);
    }

    // ----------------------------------------------------------------------

    public String getCode() {
        return code;
    }

    public LinkedHashMap<String, Object> getParams() {
        return params;
    }

    /**
     * Returns string representation of exception.
     * <p>
     * Returned string typically contains classname, message, i18n message code
     * and i18n message parameters.
     * @return string representation of exception.
     */
    public String toString() {
        StringBuilder buf = new StringBuilder();
        buf.append(getClass().getName());
        if (!StringUtils.isNullOrEmpty(getCode()) || !StringUtils.isNullOrEmpty(getMessage())
                || getParams() != null && !getParams().isEmpty()) {
            buf.append(": ");
        }
        if (!StringUtils.isNullOrEmpty(getCode())) {
            buf.append("(").append(getCode()).append(") ");
        }
        if (!StringUtils.isNullOrEmpty(getMessage())) {
            buf.append(getMessage());
        }
        if (getParams() != null && !getParams().isEmpty()) {
            buf.append(" (").append(CollectionUtils.mapToString(getParams(), ": ", ", ", true)).append(
                    ")");
        }
        return buf.toString();
    }

    // ----------------------------------------------------------------------

    /**
     * Creates ordered map from given params, which will be used as map values.
     * map keys will be named 'param0', 'param1', ....
     * @param params an array of parameters.
     * @return map of parameters.
     */
    static LinkedHashMap<String, Object> paramsToMap(Object... params) {
        LinkedHashMap<String, Object> out;
        if (params == null || params.length == 0) {
            out = new LinkedHashMap<String, Object>(0);
        } else {
            out = new LinkedHashMap<String, Object>(params.length);
            for (int i = 0; i < params.length; i++) {
                Object param = params[i];
                out.put("param" + i, param);
            }
        }
        return out;
    }

}
